package mdt.action;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.tigris.gef.base.Globals;
import org.tigris.gef.base.Layer;
import org.tigris.gef.base.ModeCreatePolyEdge;
import org.tigris.gef.graph.GraphModel;
import org.tigris.gef.graph.MutableGraphModel;
import org.tigris.gef.presentation.Fig;
import org.tigris.gef.presentation.FigEdge;
import org.tigris.gef.presentation.FigNode;
import org.tigris.gef.undo.UndoManager;
import mdt.figure.FigShapedEdge;
import mdt.model.ConnectorAnchor;

import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

/**
 * Created by lambert on 2016/6/18.
 */
public abstract class ModeCreateShapedEdge extends ModeCreatePolyEdge {
	private static Log LOG = LogFactory.getLog(ModeCreateShapedEdge.class);
	private boolean enableSelfEdge = true;
	private Point firstPoint;
	public Fig createNewItem(MouseEvent me, int snapX, int snapY) {

		//FigRect rect = new FigRect(snapX, snapY, 0, 0, Globals.getPrefs().getRubberbandColor(), Color.white);
		_startX = _lastX = snapX;
		_startY = _lastY = snapY;
		Fig fig = createdNewItemImpl();
		fig.setLocation(snapX, snapY);
		firstPoint = new Point(snapX, snapY);
		return fig;
	}

	protected abstract Fig createdNewItemImpl();

	public boolean isEnableSelfEdge() {
		return enableSelfEdge;
	}

	public void setEnableSelfEdge(boolean enableSelfEdge) {
		this.enableSelfEdge = enableSelfEdge;
	}

	protected Object getAnchorNode() {
		return new ConnectorAnchor();
	}

	public void mouseReleased(MouseEvent me) {
		if (me.isConsumed()) {
			return;
		}
		FigNode srcFigNode = getSourceFigNode();
		if (srcFigNode == null && !enableSelfEdge && !Globals.curEditor().isFreeEdgeMode()) {
			done();
			me.consume();
			return;
		}

		UndoManager.getInstance().startChain();

		int x = me.getX(), y = me.getY();
		Fig dstFig = editor.hit(x, y);
		if (dstFig == null) {
			dstFig = editor.hit(x - 16, y - 16, 32, 32);
		}

		GraphModel graphModel = editor.getGraphModel();
		if (!(graphModel instanceof MutableGraphModel)) {
			dstFig = null;
		}

		MutableGraphModel mutableGraphModel = (MutableGraphModel) graphModel;
		// needs-more-work: potential class cast exception

		Object startPort = getStartPort();
		if (dstFig == null && enableSelfEdge && srcFigNode != null) {
			dstFig = srcFigNode;
		}

		if (dstFig == null) {
			dstFig = addDstFreeModeFig(x, y);
		}

		if (dstFig instanceof FigNode) {
			FigNode destFigNode = (FigNode) dstFig;
			// If its a FigNode, then check within the
			// FigNode to see if a port exists
			Object foundPort = null;
			if (srcFigNode == destFigNode) {
				if (!enableSelfEdge) {
					done();
					me.consume();
					return;
				}
				foundPort = startPort;
			} else {
				foundPort = destFigNode.deepHitPort(x, y);
			}

			if (foundPort != null) {
				Fig destPortFig = destFigNode.getPortFig(foundPort);
				editor.damageAll();

				Class edgeClass = (Class) getArg("edgeClass");
				if (edgeClass != null)
					_newEdge = mutableGraphModel.connect(startPort, foundPort,
							edgeClass);
				else
					_newEdge = mutableGraphModel.connect(startPort, foundPort);

				// Calling connect() will add the edge to the GraphModel and
				// any LayerPersectives on that GraphModel will get a
				// edgeAdded event and will add an appropriate FigEdge
				// (determined by the GraphEdgeRenderer).

				if (_newEdge == null) {
					LOG.warn("MutableGraphModel connect() returned null");
				} else {
					srcFigNode.damage();
					destFigNode.damage();
					Layer lay = editor.getLayerManager().getActiveLayer();
					FigShapedEdge fe = (FigShapedEdge) lay.presentationFor(_newEdge);
					fe.setFig(_newItem);
					//fe.setPoint(0, getStartPortFig().getClosestPoint(firstPoint));
					//fe.setPoint(1, destPortFig.getClosestPoint(me.getPoint()));
					_newItem.setLineColor(Color.black);
					fe.setSourcePortFig(getStartPortFig());
					fe.setSourceFigNode(srcFigNode);
					fe.setDestPortFig(destPortFig);
					fe.setDestFigNode(destFigNode);

					if (fe != null) {
						editor.getSelectionManager().select(fe);
					}
					editor.damageAll();

					// if the new edge implements the MouseListener interface it
					// has to receive the mouseReleased() event
					if (fe instanceof MouseListener) {
						((MouseListener) fe).mouseReleased(me);
					}

					// set the new edge in place
					if (srcFigNode != null) {
						srcFigNode.updateEdges();
					}
					if (destFigNode != null && destFigNode != srcFigNode) {
						destFigNode.updateEdges();
					}

					endAttached(fe);
				}
				done();
				me.consume();
				return;
			}
		} else {
			done();
			me.consume();
		}
	}

	public void done() {
		if (_newEdge != null && freeModeSrcFig != null && freeModeSrcFig != null) {
			//When a shaped edge was added with two freeNode ends, we shall treat the edge as a normal node
			//instead of an edge, so we should extract the shape from the edge.
			Layer lay = editor.getLayerManager().getActiveLayer();
			FigShapedEdge fe = (FigShapedEdge) lay.presentationFor(_newEdge);
			lay.remove(fe);
			Globals.curEditor().add(_newItem);
			_newEdge = null;
		}
		super.done();
	}

	public void mouseMoved(MouseEvent me) {
		mouseDragged(me);
	}

	public void mouseDragged(MouseEvent me) {
		if (me.isConsumed())
			return;
		int x = me.getX(), y = me.getY();
		if (_newItem == null) {
			me.consume();
			return;
		}
		editor.damageAll(); // startTrans?
		Point snapPt = new Point(x, y);
		editor.snap(snapPt);

		Rectangle rect = new Rectangle(_startX, _startY, 0, 0);
		rect.add(snapPt);
		_newItem.setBounds(rect);
		editor.damageAll(); // endTrans?
		me.consume();
	}
}
